/*
 * Wazuh Vulnerability scanner
 * Copyright (C) 2015, Wazuh Inc.
 * March 25, 2023.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#ifndef _VULNERABILITY_SCANNER_FACADE_HPP
#define _VULNERABILITY_SCANNER_FACADE_HPP

#include "databaseFeedManager/databaseFeedManager.hpp"
#include "indexerConnector.hpp"
#include "messageBuffer_generated.h"
#include "policyManager/policyManager.hpp"
#include "routerSubscriber.hpp"
#include "scanOrchestrator/scanOrchestrator.hpp"
#include "singleton.hpp"
#include "socketClient.hpp"
#include <atomic>
#include <functional>
#include <memory>
#include <string>

/**
 * @brief VulnerabilityScannerFacade class.
 *
 */
class VulnerabilityScannerFacade final : public Singleton<VulnerabilityScannerFacade>
{
public:
    /**
     * @brief Starts facade.
     *
     * @param logFunction Log function.
     * @param configuration Facade configuration.
     * @param noWaitToStop If true, the facade will not wait to stop if any process is running.
     * @param reloadGlobalMapsStartup If true, the global maps will be reloaded at startup of the database feed manager.
     * @param initContentUpdater If true, the content updater will be initialized.
     */
    void start(const std::function<void(const int,
                                        const std::string&,
                                        const std::string&,
                                        const int,
                                        const std::string&,
                                        const std::string&,
                                        va_list)>& logFunction,
               const nlohmann::json& configuration,
               bool noWaitToStop = true,
               bool reloadGlobalMapsStartup = true,
               bool initContentUpdater = true);

    /**
     * @brief Stops facade.
     *
     */
    void stop();

    /**
     * @brief Decompress database content.
     *
     * @param databaseVersion Current database version.
     * @return true if the decompression was successful.
     */
    bool decompressDatabase(std::string_view databaseVersion) const;

    /**
     * @brief Initializes the alert report dispatcher.
     *
     */
    void initAlertReportDispatcher();

    /**
     * @brief Initializes the event dispatcher.
     *
     */
    void initEventDispatcher();

    /**
     * @brief Initializes the rsync subscription.
     *
     */
    void initRsyncSubscription();

    /**
     * @brief Initializes the deltas subscription.
     *
     */
    void initDeltasSubscription();

    /** @brief Initializes the Wazuh DB event subscription.
     *
     */
    void initWazuhDBEventSubscription();

    /**
     * @brief Checks the vulnerability scanner policy for changes.
     * @param stateDB RocksDBWrapper object to access to the state database.
     *
     */
    void vulnerabilityScanPolicyChange(Utils::RocksDBWrapper& stateDB) const;

    /**
     * @brief Check the cluster configuration for changes.
     * @param stateDB RocksDBWrapper object to access to the state database.
     *
     */
    void clusterConfigurationChange(Utils::RocksDBWrapper& stateDB) const;

    /**
     * @brief Performs the corresponding actions related to the policy changes.
     *
     * @note Should be called after @see clusterConfigurationChange and @see vulnerabilityScanPolicyChange methods
     */
    void handlePolicyChanges() const;

    /**
     * @brief Get seconds from epoch.
     * This method is used to get the seconds from epoch.
     * @return seconds from epoch.
     */
    static int64_t getSecondsFromEpoch()
    {
        return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch())
            .count();
    };

    /**
     * @brief push event to the event dispatcher.
     * This method is used to push an event to the event dispatcher.
     * @param message event message.
     * @param type event type.
     */
    void pushEvent(const std::vector<char>& message, BufferType type) const
    {
        flatbuffers::FlatBufferBuilder builder;
        auto object = CreateMessageBufferDirect(
            builder, reinterpret_cast<const std::vector<int8_t>*>(&message), type, getSecondsFromEpoch());

        builder.Finish(object);
        auto bufferData = reinterpret_cast<const char*>(builder.GetBufferPointer());
        size_t bufferSize = builder.GetSize();
        const rocksdb::Slice messageSlice(bufferData, bufferSize);
        m_eventDispatcher->push(messageSlice);
    }

private:
    /**
     * @brief This class models the different actions that should be performed when configurations changes are detected.
     *
     */
    class ActionWrapper
    {
    public:
        enum class Action : uint8_t
        {
            NONE,      ///< No action.
            SCAN,      ///< Perform a scan.
            CLEANUP,   ///< Perform a cleanup.
            HARD_NONE, ///< Hard none action, can't be changed.
        };

        /**
         * @brief Default constructor.
         *
         */
        explicit ActionWrapper()
            : action(Action::NONE)
        {
        }

        /**
         * @brief Constructor to initialize with an Action.
         *
         * @param a Action to initialize with.
         */
        explicit ActionWrapper(Action a)
            : action(a)
        {
        }

        /**
         * @brief Assignment operator.
         *
         * @param a Action to assign.
         * @return ActionWrapper& Reference to the ActionWrapper.
         */
        ActionWrapper& operator=(const Action& a)
        {
            if (this->action != Action::HARD_NONE)
            {
                this->action = a;
            }
            return *this;
        }

        /**
         * @brief Inequality operator.
         *
         * @param a Action to compare.
         * @return bool True if the actions are not equal, false otherwise.
         */
        bool operator!=(const Action& a) const
        {
            return this->action != a;
        }

        /**
         * @brief Equality operator.
         *
         * @param a Action to compare.
         * @return true if the actions are equal, false otherwise.
         */
        bool operator==(const Action& a) const
        {
            return this->action == a;
        }

    private:
        Action action;
    };

    void processEvent(ScanOrchestrator& scanOrchestrator, const MessageBuffer* message) const;
    std::unique_ptr<RouterSubscriber> m_syscollectorDeltasSubscription;
    std::unique_ptr<RouterSubscriber> m_syscollectorRsyncSubscription;
    std::unique_ptr<RouterSubscriber> m_wdbAgentEventsSubscription;
    std::unique_ptr<PolicyManager> m_policyManager;
    std::shared_ptr<DatabaseFeedManager> m_databaseFeedManager;
    std::shared_ptr<IndexerConnector> m_indexerConnector;
    std::shared_ptr<SocketClient<Socket<OSPrimitives, NoHeaderProtocol>, EpollWrapper>> m_reportSocketClient;
    std::shared_ptr<ReportDispatcher> m_reportDispatcher;
    std::thread m_rebootThread;
    std::thread m_managerThread;
    std::atomic<bool> m_shouldStop {false};
    mutable ActionWrapper m_agentsAction;
    mutable ActionWrapper m_managerAction;
    bool m_noWaitToStop {true};
    std::shared_ptr<EventDispatcher> m_eventDispatcher;
    std::shared_mutex m_internalMutex;
    std::condition_variable m_retryWait;
    std::mutex m_retryMutex;
};

#endif // _VULNERABILITY_SCANNER_FACADE_HPP
